﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MashGen.Engine.Expressions
{
    public class Product : AbstractExpression
    {
        public IList<AbstractExpression> Factors { get; private set; }

        private IList<AbstractExpression> normalizedFactors;

        internal IList<AbstractExpression> NormalizedFactors
        {
            get
            {
                if (normalizedFactors == null)
                {
                    normalizedFactors = Normalizer.NormalizeSummands(Factors);
                }
                return normalizedFactors;
            }
        }

        public Product(List<AbstractExpression> factors)
        {
            if (factors == null)
            {
                throw new ArgumentNullException("factors");
            }
            if (factors.Count < 2)
            {
                throw new ArgumentException("Product needs at least two factors.", "factors");
            }
            foreach (var factor in factors)
            {
                if (factor == null)
                {
                    throw new ArgumentNullException("factor");
                }
            }
            Factors = factors.AsReadOnly();
        }

        public Product(params AbstractExpression[] factors)
            : this(new List<AbstractExpression>(factors))
        {
        }

        public override R Vist<R, P>(IExpressionVisitor<R, P> visitor, P parameter = default(P))
        {
            return visitor.VisitProduct(this, parameter);
        }

        public override bool Equals(AbstractExpression other)
        {
            var otherProduct = other as Product;
            if (ReferenceEquals(otherProduct, null))
            {
                return false;
            }

            if (this.Factors.Count != otherProduct.Factors.Count)
            {
                return false;
            }

            for (int index = 0; index < Factors.Count; index++)
            {
                if (!this.NormalizedFactors[index].Equals(otherProduct.NormalizedFactors[index]))
                {
                    return false;
                }
            }
            return true;
        }

        public override int GetHashCode()
        {
            int hashCode = 0;
            foreach (var factor in Factors)
            {
                hashCode ^= factor.GetHashCode();
            }
            return 5 * hashCode;
        }
    }
}
